using System;
using System.Collections.Generic;
using WCell.Constants;
using WCell.Constants.Spells;
using WCell.RealmServer.Entities;
using WCell.RealmServer.GameObjects;
using WCell.RealmServer.Handlers;
using WCell.RealmServer.Items;
using WCell.RealmServer.Looting;
using WCell.RealmServer.NPCs;
using WCell.RealmServer.Quests;
using WCell.RealmServer.Spells;
using WCell.Util.Commands;
using WCell.Intercommunication.DataTypes;
using WCell.RealmServer.Debugging;
using WCell.RealmServer.Global;
using WCell.Util.Threading;
using WCell.Constants.World;

namespace WCell.RealmServer.Commands
{
	#region SendPacket
	public class SendPacketCommand : RealmServerCommand
	{
		protected SendPacketCommand() { }

		protected override void Initialize()
		{
			Init("SendPacket", "SendPack");
			ParamInfo = "<opcode> [<value1> [<value2> ...]]";
			EnglishDescription = "Sends any kind of Packet to the client";
		}

		public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
		{
			var opcode = trigger.Text.NextEnum(RealmServerOpCode.Unknown);
			if (opcode == RealmServerOpCode.Unknown)
			{
				trigger.Reply("Invalid OpCode.");
				return;
			}

			using (var packet = new RealmPacketOut(opcode))
			{
				while (trigger.Text.HasNext)
				{
					var val = trigger.Text.NextInt();
					packet.Write(val);
				}
				((Character)trigger.Args.Target).Client.Send(packet);
			}
		}

		public override ObjectTypeCustom TargetTypes
		{
			get
			{
				return ObjectTypeCustom.Player;
			}
		}
	}
	#endregion

	#region SendSpellMiss
	public class SendSpellMissCommand : RealmServerCommand
	{
		protected SendSpellMissCommand() { }

		protected override void Initialize()
		{
			Init("SendSpellMiss", "SendSM");
			ParamInfo = "<spellId> <0/1> <defaultReason>";
			EnglishDescription = "Sends a SpellMissLog packet to everyone in the area where you are the caster and everyone within 10y radius is the targets.";
		}

		public override RoleStatus RequiredStatusDefault
		{
			get
			{
				return RoleStatus.Admin;
			}
		}

		public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
		{
			var spell = trigger.Text.NextEnum(SpellId.None);
			var doIt = trigger.Text.NextBool();
			var reason = trigger.Text.NextEnum(CastMissReason.None);

			var objs = trigger.Args.Target.GetObjectsInRadius(10f, ObjectTypes.All, false, 0);

			var misses = new List<CastMiss>();
			foreach (var obj in objs)
			{
				if (obj != trigger.Args.Character)
				{
					misses.Add(new CastMiss(obj, reason));
				}
			}

			CombatLogHandler.SendSpellMiss(spell, trigger.Args.Character, doIt, misses);
		}

		public override bool NeedsCharacter
		{
			get
			{
				return true;
			}
		}

		public override ObjectTypeCustom TargetTypes
		{
			get
			{
				return ObjectTypeCustom.None;
			}
		}
	}
	#endregion

	#region SendBGError
	public class SendBGErrorCommand : RealmServerCommand
	{
		protected SendBGErrorCommand() { }

		protected override void Initialize()
		{
			Init("SendBGError", "SendBGErr");
			ParamInfo = "<err> [<bg>]";
		}

		public override RoleStatus RequiredStatusDefault
		{
			get { return RoleStatus.Admin; }
		}

		public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
		{
			var err = trigger.Text.NextEnum(BattlegroundJoinError.None);

			BattlegroundHandler.SendBattlegroundError(trigger.Args.Character, err);
		}

		public override bool NeedsCharacter
		{
			get { return true; }
		}

		public override ObjectTypeCustom TargetTypes
		{
			get { return ObjectTypeCustom.None; }
		}
	}
	#endregion

	#region SendGossipPOI
	public class SendGossipPOICommand : RealmServerCommand
	{
		protected SendGossipPOICommand() { }

		protected override void Initialize()
		{
			Init("POI");
			ParamInfo = "<flags> <x> <y> <extra> <name>";
			EnglishDescription = "Sends a Point of interest entry to the target (shows up on the minimap while not too close).";
		}

		public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
		{
			var chr = (Character)trigger.Args.Target;

			var flags = trigger.Text.NextEnum(GossipPOIFlags.None);
			var x = trigger.Text.NextFloat(chr.Position.X);
			var y = trigger.Text.NextFloat(chr.Position.Y);
			var extra = trigger.Text.NextInt(0);
			var name = trigger.Text.Remainder;

			if (name.Length == 0)
			{
				name = chr.Name;
			}

			GossipHandler.SendGossipPOI(chr, flags, x, y, extra, name);
		}

		public override bool NeedsCharacter
		{
			get { return true; }
		}

		public override ObjectTypeCustom TargetTypes
		{
			get { return ObjectTypeCustom.None; }
		}
	}
	#endregion

	#region ModAuras
	public class ModAurasCommand : RealmServerCommand
	{
		protected ModAurasCommand() { }

		protected override void Initialize()
		{
			base.Init("ModAuras", "MAura");
			ParamInfo = "<n> <subcommand> ...";
			EnglishDescription = "Modifies the nth Aura of the target";
		}

		public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
		{
			ProcessNth(trigger);
		}

		public class ModLevelCommand : SubCommand
		{
			protected ModLevelCommand() { }

			protected override void Initialize()
			{
				base.Init("Level", "Lvl", "L");
				ParamInfo = "<AuraLevel>";
				EnglishDescription = "Modifies the Level of the nth Aura.";
			}

			public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
			{
				var n = ((RealmServerNCmdArgs)trigger.Args).N - 1;
				var aura = trigger.Args.Target.Auras.GetAt(n);

				if (aura != null)
				{
					ModPropCommand.ModProp(aura, aura.GetType().GetProperty("Level"), trigger);
				}
				else
				{
					trigger.Reply("There aren't " + n + " Auras.");
				}
			}
		}

		public class ModFlagsCommand : SubCommand
		{
			protected ModFlagsCommand() { }

			protected override void Initialize()
			{
				base.Init("Flags", "Fl", "F");
				ParamInfo = "<AuraFlags>";
				EnglishDescription = "Modifies the Flags of the nth Aura.";
			}

			public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
			{
				var n = ((RealmServerNCmdArgs)trigger.Args).N - 1;
				var aura = trigger.Args.Target.Auras.GetAt(n);

				if (aura != null)
				{
					ModPropCommand.ModProp(aura, aura.GetType().GetProperty("Flags"), trigger);
				}
				else
				{
					trigger.Reply("There aren't " + n + " Auras.");
				}
			}
		}

		public override bool NeedsCharacter
		{
			get
			{
				return false;
			}
		}

		public override ObjectTypeCustom TargetTypes
		{
			get
			{
				return ObjectTypeCustom.Unit;
			}
		}
	}
	#endregion

	#region Load Command
	public class LoadCommand : RealmServerCommand
	{
		protected LoadCommand() { }

		protected override void Initialize()
		{
			Init("Load");
			EnglishDescription = "Loads static data from DB.";
		}

		public override RoleStatus RequiredStatusDefault
		{
			get { return RoleStatus.Admin; }
		}

		public class ItemsCommand : SubCommand
		{
			protected ItemsCommand() { }

			protected override void Initialize()
			{
				Init("Items");
				EnglishDescription = "Loads all ItemTemplates and -Spawns.";
			}

			public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
			{
				if (ItemMgr.Loaded)
				{
					trigger.Reply("Item definitions have already been loaded.");
				}
				else
				{
					RealmServer.Instance.AddMessage(() =>
					{
						trigger.Reply("Loading Items...");
						ItemMgr.LoadAll();
						trigger.Reply("Done.");
					});
				}
			}
		}


		public class GOsCommand : SubCommand
		{
			protected GOsCommand() { }

			protected override void Initialize()
			{
				base.Init("GOs");
				EnglishDescription = "Loads all GOTemplates and spawns.";
			}

			public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
			{
				if (GOMgr.Loaded)
				{
					trigger.Reply("GO definitions have already been loaded.");
				}
				else
				{
					RealmServer.Instance.AddMessage(() =>
					{
						trigger.Reply("Loading GOs...");
						GOMgr.LoadAll();

						if (Region.AutoSpawn)
						{
							RegionCommand.SpawnRegionCommand.SpawnAllRegions(trigger);
						}
						trigger.Reply("Done.");
					});
				}
			}
		}


		public class NPCsCommand : SubCommand
		{
			protected NPCsCommand() { }

			protected override void Initialize()
			{
				Init("NPCs");
				ParamInfo = "[esw]";
				EnglishDescription = "Loads all NPC definitions from files and/or DB. e: Load entries; s: Load Spawns; w: Load Waypoints (together with s)";
			}

			public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
			{
				if (NPCMgr.Loaded)
				{
					trigger.Reply("NPC definitions have already been loaded.");
				}
				else
				{
					RealmServer.Instance.AddMessage(() =>
					{
						trigger.Reply("Loading NPCs...");
						if (!trigger.Text.HasNext)
						{
							NPCMgr.LoadNPCDefs();
						}
						else
						{
							var word = trigger.Text.NextWord();
							if (word.Contains("e"))
							{
								NPCMgr.LoadEntries();
							}
							if (word.Contains("s"))
							{
								NPCMgr.OnlyLoadSpawns();
							}
							if (word.Contains("w"))
							{
								NPCMgr.LoadWaypoints();
							}
						}

						if (Region.AutoSpawn)
						{
							RegionCommand.SpawnRegionCommand.SpawnAllRegions(trigger);
						}

						trigger.Reply("Done.");
					});
				}
			}
		}


		public class LoadQuestCommand : SubCommand
		{
			protected LoadQuestCommand() { }

			protected override void Initialize()
			{
				base.Init("Quests");
				EnglishDescription = "Loads all Quest definitions from files and/or DB.";
			}

			public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
			{
				if (QuestMgr.Loaded)
				{
					trigger.Reply("Quest definitions have already been loaded.");
				}
				else
				{
					RealmServer.Instance.AddMessage(() =>
					{
						trigger.Reply("Loading Quests...");
						QuestMgr.LoadAll();
						trigger.Reply("Done.");
					});
				}
			}
		}

		public class LootCommand : SubCommand
		{
			protected LootCommand() { }

			protected override void Initialize()
			{
				base.Init("Loot");
				EnglishDescription = "Loads all Loot from files and/or DB.";
			}

			public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
			{
				if (LootMgr.Loaded)
				{
					trigger.Reply("Loot has already been loaded.");
				}
				else
				{
					RealmServer.Instance.AddMessage(() =>
					{
						trigger.Reply("Loading Loot...");
						LootMgr.LoadAll();
						trigger.Reply("Done.");
					});
				}
			}
		}

		public class AllCommand : SubCommand
		{
			protected AllCommand() { }

			protected override void Initialize()
			{
				Init("All");
				ParamInfo = "[-w]";
				EnglishDescription = "Loads all static content definitions from DB. "
						+ "The -w switch will ensure that execution (of the current Region) won't continue until Loading finished.";
			}

			public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
			{
				if (trigger.Text.NextModifiers() == "w")
				{
					LoadAll(trigger);
				}
				else
				{
					RealmServer.Instance.AddMessage(() => LoadAll(trigger));
				}
			}

			void LoadAll(CmdTrigger<RealmServerCmdArgs> trigger)
			{
				var start = DateTime.Now;

				try
				{
					if (NPCMgr.Loaded)
					{
						trigger.Reply("NPC definitions have already been loaded.");
					}
					else
					{
						trigger.Reply("Loading NPCs...");
						NPCMgr.LoadNPCDefs();
						trigger.Reply("Done.");
					}
				}
				catch (Exception ex)
				{
					FailNotify(trigger, ex);
				}

				try
				{
					if (GOMgr.Loaded)
					{
						trigger.Reply("GO definitions have already been loaded.");
					}
					else
					{
						trigger.Reply("Loading GOs...");
						GOMgr.LoadAll();
						trigger.Reply("Done.");
					}
				}
				catch (Exception ex)
				{
					FailNotify(trigger, ex);
				}

				try
				{
					if (ItemMgr.Loaded)
					{
						trigger.Reply("Item definitions have already been loaded.");
					}
					else
					{
						trigger.Reply("Loading Items...");
						ItemMgr.LoadAll();
						trigger.Reply("Done.");
					}
				}
				catch (Exception ex)
				{
					FailNotify(trigger, ex);
				}


				try
				{
					if (QuestMgr.Loaded)
					{
						trigger.Reply("Quest definitions have already been loaded.");
					}
					else
					{
						trigger.Reply("Loading Quests...");
						QuestMgr.LoadAll();
						trigger.Reply("Done.");
					}
				}
				catch (Exception ex)
				{
					FailNotify(trigger, ex);
				}

				try
				{
					if (LootMgr.Loaded)
					{
						trigger.Reply("Loot has already been loaded.");
					}
					else
					{
						trigger.Reply("Loading Loot...");
						LootMgr.LoadAll();
						trigger.Reply("Done.");
					}
				}
				catch (Exception ex)
				{
					FailNotify(trigger, ex);
				}

				trigger.Reply("All done - Loading took: " + (DateTime.Now - start));
				GC.Collect(2, GCCollectionMode.Optimized);

				if (Region.AutoSpawn)
				{
					RegionCommand.SpawnRegionCommand.SpawnAllRegions(trigger);
				}
			}
		}
	}
	#endregion

	#region ForgetSelf
	public class ForgetSelfCommand : RealmServerCommand
	{
		protected override void Initialize()
		{
			Init("ForgetSelf");
			ParamInfo = "";
		}

		public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
		{
			var chr = (Character)trigger.Args.Target;
			var pos = chr.Position;

			MovementHandler.SendNewWorld(chr.Client, chr.RegionId, ref pos, chr.Orientation);
			chr.ClearSelfKnowledge();
		}

		public override ObjectTypeCustom TargetTypes
		{
			get
			{
				return ObjectTypeCustom.Player;
			}
		}
	}
	#endregion

	#region World States
	public class WSCommand : RealmServerCommand
	{
		protected override void Initialize()
		{
			Init("WorldState", "WS");
		}

		public class InitWSCommand : SubCommand
		{
			protected override void Initialize()
			{
				Init("Init", "I");
				ParamInfo = "<area> [<state> <value>[ <state2> <value2> ...]]";
			}

			public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
			{
				var chr = (Character)trigger.Args.Target;
				var area = trigger.Text.NextInt(-1);
				if (area < 0)
				{
					trigger.Reply("No area given");
					return;
				}
				var states = new List<WorldState>();
				while (trigger.Text.HasNext)
				{
					var key = trigger.Text.NextEnum(WorldStateId.End);
					if (key == WorldStateId.End)
					{
						trigger.Reply("Found invalid state.");
						return;
					}
					var value = trigger.Text.NextInt();
					//states.Add(new WorldState(key, value));
				}
				MiscHandler.SendInitWorldStates(chr, chr.Region.Id, chr.ZoneId, (uint)area, states.ToArray());
			}
		}

		public override ObjectTypeCustom TargetTypes
		{
			get
			{
				return ObjectTypeCustom.Player;
			}
		}
	}
	#endregion

	#region Debug
	public class DebugCommand : RealmServerCommand
	{
		public override RoleStatus RequiredStatusDefault
		{
			get { return RoleStatus.Admin; }
		}

		protected override void Initialize()
		{
			Init("Debug");
			EnglishDescription = "Provides Debug-capabilities and management of Debug-tools for Devs.";
		}

		public class ReloadPADefsCommand : SubCommand
		{
			protected override void Initialize()
			{
				Init("ReloadDefs", "Reload");
				EnglishDescription = "Reloads the Packet-definitions for the Packet Analyzer.";
			}

			public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
			{
				DebugUtil.LoadDefinitions();
				trigger.Reply("Packet definitions have been reloaded.");
			}
		}

		public class DefsCommand : SubCommand
		{
			protected override void Initialize()
			{
				Init("GC");
				EnglishDescription = "Performs Garbage Collection.";
			}

			public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
			{
				GC.Collect();
				trigger.Reply("Packet definitions have been reloaded.");
			}
		}
	}
	#endregion

	public class IOWaitCommand : RealmServerCommand
	{
		public override RoleStatus RequiredStatusDefault
		{
			get { return RoleStatus.Admin; }
		}

		protected override void Initialize()
		{
			Init("IOWait");
			EnglishDescription = "Lets the current Thread wait for the next tick of the IO Queue. " +
				"Don't use on public servers if you don't know what you are doing!";
		}

		public override void Process(CmdTrigger<RealmServerCmdArgs> trigger)
		{
			RealmServer.Instance.WaitOne();
		}
	}
}
